package com.hzit.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.MD5;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.hzit.user.dto.UserDTO;
import com.hzit.user.mapper.UserMapper;
import com.hzit.user.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.system.api.domain.User;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.ReactiveScriptExecutor;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author wf
 * @since 2024-03-20
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
	@Autowired
	private StringRedisTemplate redisTemplate;
	@Autowired
	private RocketMQTemplate mqTemplate;
	@Autowired
	private RedissonClient redissonClient;

	//1. 收到手机号，保存验证码到redis后并发送验证码出去给中间件的监听方接受
	@Override
	public void createCode(String phone) {
		//1.1 生成一个6位数的数字验证码
		String code = RandomUtil.randomNumbers(6);
		//1.2 保存到redis中
		redisTemplate.opsForValue().set(phone, code, 10, TimeUnit.DAYS);
		//1.3 通过消息中间件发送消息给消息监听方，则监听方再通过阿里云发送验证码到手机上
		Map map = new HashMap<>();
		map.put("phone", phone);
		map.put("code", code);
		mqTemplate.convertAndSend("hzit-topic", map);
	}

	// 2. 添加用户
	@Override
	public boolean add(UserDTO dto) {
		//2.1 比较用户输入的验证码与redis中的是否一致
		String code = redisTemplate.opsForValue().get(dto.getPhone());
		if (dto.getValidCode().equals(code)) {
			//2.2 转换dto为User对象
			User user = new User();
			String password = dto.getPassword();
			String pwd = SecureUtil.md5(password);
			BeanUtil.copyProperties(dto, user, true);
			user.setPassword(pwd);
			user.setCreated(LocalDateTime.now());
			user.setUpdated(LocalDateTime.now());
			//2.2 保存用户对象
			this.save(user);
			return true;
		}
		return false;

	}

	//1. 查询所有用户
	// 分布式锁方案一：使用lua脚本 + redis实现 (setNX)
	// 缺点：不能自动续期
	@Override
	public List<User> findAll() {
		List<User> users = null;
		//1.1 生成一个随机的字符串
		String uuid = UUID.randomUUID().toString().replace("-", "");
		//1.2 执行setNX命令，让它生生互斥性
		Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 30, TimeUnit.SECONDS);
		//1.3 判断是否有锁
		if (lock) {
			try {
				//1.1 从redis中查询所有用户
				String str = redisTemplate.opsForValue().get("hzit-users");
				if (StrUtil.isNotBlank(str)) {
					System.out.println("正在从缓存获取数据...");
					users = JSON.parseArray(str, User.class);
				} else {
					System.out.println("正在从数据库中查询数据...");
					users = this.list();
					// 保存到redis中
					redisTemplate.opsForValue().set("hzit-users", JSON.toJSONString(users));
				}
			} finally {
				// 定义脚本字符串
				String scriptStr = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
				// 定义脚本对象
				RedisScript<Long> script = new DefaultRedisScript<>(scriptStr,Long.class);
				// 执行脚本
				redisTemplate.execute(script, Arrays.asList("lock"), uuid);
			}
		} else {
			// 其它线程通过自旋获取锁
			findAll();
		}
		return users;
	}

	/**
	 * 分布锁的实现方案二：reidsson框架
	 * @return
	 */
	@Override
	public List<User> findAll2() {
		List<User> users;
		//1.1 得到锁
		RLock lock = redissonClient.getLock("lock");
		lock.lock();                                // 如果没有指定参数就会使用看门狗机制，自动续期时间
		// lock.lock(30,TimeUnit.MICROSECONDS);     // 如果我们设置了业务执行时间，此时相当于30ms执行不完成就会自动解锁！
		try{
			//1.1 从redis中查询所有用户
			String str = redisTemplate.opsForValue().get("hzit-users");
			if (StrUtil.isNotBlank(str)) {
				System.out.println("正在从缓存获取数据(findAll2)...");
				users = JSON.parseArray(str, User.class);
			} else {
				System.out.println("正在从数据库中查询数据(findAll2)...");
				users = this.list();
				// 保存到redis中
				redisTemplate.opsForValue().set("hzit-users", JSON.toJSONString(users));
			}
		}finally {
			lock.unlock();
		}
		return users;
	}

	@Transactional      // 开启一个分支事务
	@Override
	public void saveUser() {
		int i = 10 / 0;
		User user = new User();
		user.setUsername("小明");
		user.setPassword("123");
		user.setCreated(LocalDateTime.now());
		user.setUpdated(LocalDateTime.now());
		this.save(user);


	}


}
